// https://promisesaplus.com/

// promise三种状态 pending，fulfilled，rejected
enum PromiseState {
	PENDING = 'pending',
	FULFILLED = 'fulfilled',
	REJECTED = 'rejected',
}

type PromiseResolve<T = any> = (value?: T) => void;

type PromiseReject<T = any> = (reason?: T) => void;

type PromiseExecutor<T = any> = (resolve?: PromiseResolve<T>, reject?: PromiseReject<T | any>) => any;

type PromiseThen<T = any> = (onResolved?: PromiseResolve<T>, onRejected?: PromiseReject<T | any>) => MyPromise<T | any>;

type PromiseCatch<T = any> = (onRejected?: PromiseReject<T | any>) => MyPromise<T | any>;

type PromiseFinally<T = any> = (handler?: (value?: any) => any) => MyPromise<T | any>;

type PromiseStaticAll = (iterable: MyPromise<any>[]) => MyPromise<any[]>;
type PromiseStaticRace = (iterable: MyPromise<any>[]) => MyPromise<any>;
type PromiseStaticResolve<T = any> = (value: T) => MyPromise<T>;
type PromiseStaticReject<T = any> = (value: T) => MyPromise<T>;

type PromiseStaticDeferred = () => {
	promise: MyPromise<any>;
	resolve: PromiseResolve<any>;
	reject: PromiseReject<any>;
};

/**
 * promise 处理程序是一个表现形式为 [[Resolve]](promise, x) 的抽象处理操作。
 * 如果 x 是 thenable 类型，它会尝试生成一个 promise 处理 x
 *
 * @template T
 * @param {MyPromise<T>} promise
 * @param {*} x
 * @param {PromiseResolve<T>} resolve
 * @param {(PromiseReject<T | any>)} reject
 * @returns
 */
const resolvePromise = <T = any>(promise: MyPromise<T>, x: any, resolve: PromiseResolve<T>, reject: PromiseReject<T | any>) => {
	// 2.3.1 如果返回的promise 和 x 是指向同一个引用（循环引用），则抛出错误
	if (promise === x) {
		return reject(new TypeError('Chaining cycle detected for promise'));
	}

	// 如果 x 是一个 promise 实例，则采用它的状态
	if (x instanceof MyPromise) {
		// 2.3.2.1 如果 x 是 pending 状态，那么保留它（递归执行这个 promise 处理程序），直到 pending 状态转为 fulfilled 或 rejected 状态.
		if (x.currentState === PromiseState.PENDING) {
			x.then((value: any) => {
				resolvePromise(promise, value, resolve, reject);
			}, reject);
		} else {
			x.then(resolve, reject);
		}
		return;
	}
	// 2.3.3 如果 x 是个对象或函数类型
	if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
		// 2.3.3.3.3 如果 resolvePromise 和 rejectPromise 已经被调用或以相同的参数多次调用的话吗，优先第一次的调用，并且之后的调用全部被忽略（避免多次调用）
		let hasCalled = false;
		try {
			// 2.3.3.1 把 x.then 赋值给 then 变量
			const then = x.then;
			// 2.3.3.3 如果 then 是函数类型，那个用 x 调用它（将 then 的 this 指向 x）,第一个参数传 resolvePromise ，第二个参数传 rejectPromise：
			if (typeof then === 'function') {
				then.call(
					x,
					// 2.3.3.3.1 如果或当 resolvePromise 被调用并接受一个参数 y 时，执行 [[Resolve]](promise, y)
					(y: any) => {
						if (hasCalled) return;
						hasCalled = true;
						resolvePromise(promise, y, resolve, reject);
					},
					// 2.3.3.3.2 如果或当 rejectPromise 被调用并接受一个参数 r 时，执行 reject(r)
					(r: any) => {
						if (hasCalled) return;
						hasCalled = true;
						reject(r);
					},
				);
			} else {
				resolve(x);
			}
			// 2.3.3.2 如果捕获到 x.then 抛出的错误的话，需要 reject 这个promise
		} catch (error) {
			// 2.3.3.3.4.1 如果 resolvePromise 或 rejectPromise 已经被调用，那么忽略异常
			if (hasCalled) return;
			hasCalled = true;
			// 2.3.3.3.4.2 否则，reject 这个异常
			reject(error);
		}
	} else {
		// 2.3.3.4 如果 then 不是函数类型，直接 resolve x（resolve(x)）
		resolve(x);
	}
};

class MyPromise<T = any> {
	// promise 值
	value: T;
	// promise 状态
	currentState: PromiseState = PromiseState.PENDING;
	// 成功回调队列
	resolvedCallbacks: PromiseResolve[] = [];
	// 失败回调队列
	rejectedCallbacks: PromiseReject[] = [];

	// 传入函数，接收两个方法改变状态
	constructor(fn: PromiseExecutor<T>) {
		try {
			fn(this.resolve, this.reject);
		} catch (e) {
			this.reject(e);
		}
	}

	resolve: PromiseResolve<T> = (value?: T) => {
		if (value instanceof MyPromise) {
			return value.then(this.resolve, this.reject);
		}
		setTimeout(() => {
			if (this.currentState === PromiseState.PENDING) {
				this.currentState = PromiseState.FULFILLED;
				this.value = value;
				this.resolvedCallbacks.forEach((cb) => cb());
			}
		});
	};

	reject: PromiseReject<T> = (reason?: T) => {
		setTimeout(() => {
			if (this.currentState === PromiseState.PENDING) {
				this.currentState = PromiseState.REJECTED;
				this.value = reason;
				this.rejectedCallbacks.forEach((cb) => cb());
			}
		});
	};

	/**
	 *一个 promise 必须提供一个 then 方法，用来获取当前或最终的 value 或 reason
	 * 2.2.7 then 方法一定返回一个 promise
	 * @param {PromiseResolve<T>} [onFulfilled]
	 * @param {PromiseReject<T>} [onRejected]
	 * @returns {MyPromise<T>}
	 */
	then: PromiseThen<T> = (onFulfilled?: PromiseResolve<T>, onRejected?: PromiseReject<T>): MyPromise<T> => {
		let promise: MyPromise;
		// 2.2.1.1 如果 onFulfilled 不是函数，它会被忽略
		const _onFulfilled: any = typeof onFulfilled === 'function' ? onFulfilled : (value: any) => value;
		// 2.2.1.2 如果 onRejected 不是函数，它会被忽略
		const _onRejected: any =
			typeof onRejected === 'function'
				? onRejected
				: (reason: any) => {
						throw reason;
				  };
		// 2.2.6 promise 的 then 可以链式调用多次
		if (this.currentState === PromiseState.PENDING) {
			promise = new MyPromise<T>((resolve, reject) => {
				// 2.2.6.1 如果或当 promise 转态是 fulfilled 时，所有的 onFulfilled 回调回以他们注册时的顺序依次执行
				this.resolvedCallbacks.push(() => {
					try {
						const x = _onFulfilled(this.value);
						resolvePromise(promise, x, resolve, reject);
					} catch (r) {
						reject(r);
					}
				});
				// 2.2.6.2 如果或当 promise 转态是 rejected 时，所有的 onRejected 回调回以他们注册时的顺序依次执行
				this.rejectedCallbacks.push(() => {
					try {
						const x = _onRejected(this.value);
						resolvePromise(promise, x, resolve, reject);
					} catch (r) {
						reject(r);
					}
				});
			});
			return promise;
		}

		// 2.2.2 如果 onFulfilled 是一个函数：
		// 2.2.2.1 它一定是在 promise 是 fulfilled 状态后调用，并且接受一个参数 value
		if (this.currentState === PromiseState.FULFILLED) {
			promise = new MyPromise<T>((resolve, reject) => {
				// 2.2.5 onFulfilled 和 onRejected 会作为函数形式调用 (也就是说，默认 this 指向 global，严格模式 undefined) [3.2]
				setTimeout(() => {
					try {
						const x = _onFulfilled(this.value);
						resolvePromise(promise, x, resolve, reject);
					} catch (reason) {
						reject(reason);
					}
				});
			});
			return promise;
		}

		// rejected状态
		if (this.currentState === PromiseState.REJECTED) {
			promise = new MyPromise<T>((resolve, reject) => {
				// 2.2.5 onFulfilled 和 onRejected 会作为函数形式调用 (也就是说，默认 this 指向 global，严格模式 undefined) [3.2]
				setTimeout(() => {
					try {
						const x = _onRejected(this.value);
						resolvePromise(promise, x, resolve, reject);
					} catch (reason) {
						reject(reason);
					}
				});
			});
			return promise;
		}
	};

	// Promise.prototype.catch 用于指定出错时的回调，是特殊的then方法，catch之后，可以继续 .then
	catch: PromiseCatch<T> = (onRejected: PromiseReject<T>) => {
		return this.then(null, onRejected);
	};

	// 不管成功还是失败，都会走到finally中,并且finally之后，还可以继续then。并且会将值原封不动的传递给后面的then.
	finally: PromiseFinally<T> = (onFinally) => {
		return this.then(
			(value) => MyPromise.resolve(onFinally()).then(() => value),
			(reason) =>
				MyPromise.resolve(onFinally()).then(() => {
					throw reason;
				}),
		);
	};

	/**
	 * Promise.all 接收一个promise数组,
	 * 只有数组中全部的 Promise 都变为 resolve 的时候返回一个新的 Promise 实例，
	 * 只要有一个失败，状态就变成 rejected
	 *
	 * @param {MyPromise<any>[]} iterable
	 * @returns
	 */
	static all: PromiseStaticAll = (iterable: MyPromise<any>[]) => {
		return new MyPromise<any[]>((resolve, reject) => {
			const result: any[] = [];
			iterable.forEach((promiseInstance, index) => {
				promiseInstance.then((value) => {
					result[index] = value;
					if (result.length === iterable.length) {
						resolve(result);
					}
				}, reject);
			});
		});
	};

	/**
	 *  Promise.race(iterable)，接收一个promise数组
	 * 当iterable参数里的任意一个子promise被成功或失败后，、父promise马上也会用子promise的成功返回值或失败
	 *
	 * @param {MyPromise<any>[]} iterable
	 * @returns
	 */
	static race: PromiseStaticRace = (iterable: MyPromise<any>[]) => {
		return new MyPromise<any[]>((resolve, reject) => {
			iterable.forEach((promiseInstance) => {
				promiseInstance.then((value) => resolve(value), reject);
			});
		});
	};

	static resolve: PromiseStaticResolve = <T>(value: T) => {
		const promise = new MyPromise<T>((resolve, reject) => {
			resolvePromise(promise, value, resolve, reject);
		});
		return promise;
	};

	static reject: PromiseStaticReject = <T>(value: T) => {
		return new MyPromise<T>((_resolve, reject) => {
			reject(value);
		});
	};

	// promiseA+ 测试用
	static deferred: PromiseStaticDeferred = () => {
		const result: any = {};
		result.promise = new MyPromise((resolve, reject) => {
			result.resolve = resolve;
			result.reject = reject;
		});
		return result;
	};
}

export default MyPromise;
